package controller;

import group.GeoGroup;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;

import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import com.sun.j3d.utils.picking.PickIntersection;
import com.sun.j3d.utils.picking.PickResult;

import component.AbstractComponent;
import component.Ball;
import component.ComponentFactory;
import component.Status;
import component.Structure;
import component.Tube;
import controller.camera.Camera;

import math.Spherical;


public class PlaceFreeController extends AbstractController {

	private final Structure structure;
	private final ComponentFactory factory;
	private final Camera camera;
	
	private Ball ball = null;
	private Tube tube = null;
	
	private AbstractComponent hoverOver = null;
	
	private boolean ballActive = false;
	private boolean tubeActive = false;
	
	@Deprecated
	public PlaceFreeController(Canvas3D canvas, BranchGroup bg, GeoGroup geoNodes, GeoGroup tempNodes, Bounds bounds) {
		super(canvas, bg);
		structure = null;
		factory = null;
		camera = null;
	}
	
	
	public PlaceFreeController(Canvas3D canvas, BranchGroup pickableBranch, Structure structure, ComponentFactory factory, Camera camera) {
		super(canvas, pickableBranch);
		this.structure = structure;
		this.factory = factory;
		this.camera = camera;
		createNewBall();
		createNewTube();
		notOverAnything();
	}
	
	private void createNewBall() {
		ball = factory.createBall(new Point3d(0, 0, 0));
		ball.setStatus(Status.ILLEGAL | Status.TRANSPARENT);
	}
	
	private void createNewTube() {
		tube = factory.createTube(new Point3d(0, 0, 0), new Spherical(0, 0));
		tube.setStatus(Status.ILLEGAL | Status.TRANSPARENT);
	}
	
	private void notOverAnything() {
		ball.detachFromScene();
		tube.detachFromScene();
		ballActive = false;
		tubeActive = false;
		hoverOver = null;
	}
	

	@Override
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent e) {
		notOverAnything();

	}

	@Override
	public void mousePressed(MouseEvent e) {
		if (e.getButton() == MouseEvent.BUTTON1 && hoverOver != null) {
			if (ballActive && ball.isValidPosition() && ball.canConnectTo(hoverOver)) {
				ball.setStatus(Status.NORMAL);
				hoverOver.connect(ball);
				structure.transferToPermanent(ball);
				createNewBall();
			}
			
			if (tubeActive && tube.isValidPosition() && tube.canConnectTo(hoverOver)) {
				tube.setStatus(Status.NORMAL);
				hoverOver.connect(tube);
				structure.transferToPermanent(tube);
				createNewTube();
			}
		
		}
		notOverAnything();
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseDragged(MouseEvent e) {
		
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		PickResult result = this.pickClosest(e);
		AbstractComponent component = this.getComponent(result);
		if (component == null) {
			notOverAnything();
		} else {
			if (component instanceof Ball) {
				hoverOverBall((Ball)component, result);
			} else if (component instanceof Tube) {
				hoverOverTube((Tube)component, result);
			} else {
				notOverAnything();
			}
		}
	}
	
	private PickIntersection getClosestIntersection(PickResult result) {
		return result.getClosestIntersection(camera.getPos());
	}

	private void hoverOverTube(Tube tube, PickResult result) {
		if (tubeActive) {
			notOverAnything();
		}
		
		this.hoverOver = tube;
		Point3d hoverPoint = this.getClosestIntersection(result).getPointCoordinatesVW();
		Point3d[] freePlaces = tube.getFreeConnectionPositions();
		if (freePlaces.length == 0) {
			notOverAnything();
			return;
		}
		Point3d closest = null;
		for (Point3d free : freePlaces) {
			if (closest == null) {
				closest = free;
			} else {
				if (hoverPoint.distance(free) < hoverPoint.distance(closest)) {
					closest = free;
				}
			}
		}
		ball.setPos(closest);
		if (!ballActive) structure.addNonpickable(ball);
		ballActive = true;
		if (ball.isValidPosition())
			ball.setStatus(Status.PLACEMENT | Status.TRANSPARENT);
		else
			ball.setStatus(Status.ILLEGAL | Status.TRANSPARENT);
	}


	private void hoverOverBall(Ball ball, PickResult result) {
		if (ballActive) {
			notOverAnything();
		}
		
		hoverOver = ball;
		Point3d hoverPoint = this.getClosestIntersection(result).getPointCoordinatesVW();
		Spherical orientation = new Spherical();
		Vector3d hoverVector = new Vector3d();
		hoverVector.sub(hoverPoint, ball.getPos());
		orientation.setVector(hoverVector);
		tube.setPos(ball.getPos());
		tube.setOrientation(orientation);
		if (!tubeActive) structure.addNonpickable(tube);
		tubeActive = true;
		if (tube.isValidPosition())
			tube.setStatus(Status.PLACEMENT | Status.TRANSPARENT);
		else
			tube.setStatus(Status.ILLEGAL | Status.TRANSPARENT);
	}


	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void keyPressed(KeyEvent e) {
		

	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void activate() {
	}

	@Override
	public void deactivate() {
		notOverAnything();
	}
}
